package org.luxor.commons.redis.plugin.idempotent.service.impl;

import org.luxor.commons.core.exception.ParamIllegalException;
import org.luxor.commons.core.utils.StringUtils;
import org.luxor.commons.core.utils.UuidUtils;
import org.luxor.commons.redis.client.RedisStringClient;
import org.luxor.commons.redis.component.RedisKeys;
import org.luxor.commons.redis.plugin.idempotent.exception.IdempotentException;
import org.luxor.commons.redis.plugin.idempotent.service.IdempotentService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.util.Assert;

/**
 * 幂等控制功能核心服务(token生成器)
 *
 * @author Mr.Yan  @date: 2020/9/18
 */
public class IdempotentServiceImpl implements IdempotentService {
    private static final Logger logger = LoggerFactory.getLogger(IdempotentServiceImpl.class);

    /**
     * 幂等token有效期默认7小时
     */
    private static final Integer DEF_EXPIRE_SECONDS = 7 * 60 * 60;

    /**
     * 建议幂等token的最小有效期
     */
    private static final int MIN_EXPIRE_SECONDS = 15 * 60;

    private final RedisStringClient redisStringClient;

    public IdempotentServiceImpl(RedisStringClient redisStringClient) {
        Assert.notNull(redisStringClient, "Spring容器中找不到[redisStringClient]的实例化对象!");
        this.redisStringClient = redisStringClient;
    }

    @Override
    public String createToken(String businessCode) {
        if (StringUtils.isBlank(businessCode)) {
            throw new ParamIllegalException("businessCode is null!");
        }
        String token = UuidUtils.uuid();
        String redisKey = RedisKeys.builder("idempotent").append(token).build();
        redisStringClient.set(redisKey, businessCode, DEF_EXPIRE_SECONDS);
        return token;
    }

    @Override
    public String createToken(String businessCode, int expireSeconds) {
        if (StringUtils.isBlank(businessCode)) {
            throw new ParamIllegalException("businessCode is null!");
        }
        if (expireSeconds < MIN_EXPIRE_SECONDS) {
            logger.warn("幂等token有效期的设置，建议大于" + MIN_EXPIRE_SECONDS + ",避免过早失效导致校验失败！");
        }
        String token = UuidUtils.uuid();
        String limitRateKey = RedisKeys.builder("idempotent").append(token).build();
        redisStringClient.set(limitRateKey, businessCode, expireSeconds);
        return token;
    }

    @Override
    public void register(String businessCode, String token) {
        if (StringUtils.isBlank(businessCode)) {
            throw new ParamIllegalException("businessCode is null!");
        }
        if (StringUtils.isBlank(token)) {
            throw new ParamIllegalException("token is null!");
        }
        String redisKey = RedisKeys.builder("idempotent").append(token).build();
        redisStringClient.set(redisKey, businessCode, DEF_EXPIRE_SECONDS);
    }

    @Override
    public void assertPass(String businessCode, String token) throws IdempotentException {
        if (StringUtils.isBlank(businessCode)) {
            throw new ParamIllegalException("businessCode is null!");
        }
        String limitRateKey = RedisKeys.builder("idempotent").append(token).build();
        String businessCodeValue = redisStringClient.get(limitRateKey);
        if (businessCode.equals(businessCodeValue)) {
            redisStringClient.delete(limitRateKey);
            return;
        }
        throw new IdempotentException("invalid idempotent token");
    }

    @Override
    public boolean questionPass(String businessCode, String token) {
        if (StringUtils.isBlank(businessCode)) {
            throw new ParamIllegalException("businessCode is null!");
        }
        String limitRateKey = RedisKeys.builder("idempotent").append(token).build();
        String businessCodeValue = redisStringClient.get(limitRateKey);
        if (businessCode.equals(businessCodeValue)) {
            redisStringClient.delete(limitRateKey);
            return true;
        }
        return false;
    }

}
